<?php
/**
* hsync server
*author duwei<duv123@gmail.com>
*/
define('DS',DIRECTORY_SEPARATOR);
define("HSYNC_VERSION", "2012-04-21");
header("version:".HSYNC_VERSION);
/*
$debug=false;
if($debug){
  $_POST['user']='user01';
  $_POST['token']=md5('psw01');
  $_POST['remote']="/home/duwei/Desktop/tmp";
}
*/
$data=$_POST;

if(get_magic_quotes_gpc()){
   foreach ($data as $k=>$v){
      $data[$k]=stripslashes($v);
    }
}
if(!empty($data['content'])){
  $data['content']=urldecode($data['content']);
}
$fn=isset($data['fn'])?$data['fn']:null;

if('version'==$fn){
  echo HSYNC_VERSION;
  die;
}

//---------------------------------------
//用户信息认证
$users=require('./config/user.php');
if(!isset($_POST['user']) || !isset($_POST['token'])){
  die_error('user info miss!');
}
if(!isset($users[$_POST['user']])){
  die_error('unknown user');
}


$token=$data['token'];
unset($data['token']);
$_token=md5($users[$data['user']]['psw'].$data['client_time']);
if($token != $_token){
   die_error('authentication failed['.$token."][".$_token."]");
}
//========================================

$config=require('./config/config.php');

$fileName=($data['remote']);

function isInSafeDir($fileName){
  if(false !== strpos($fileName,'..'))return false;//不允许出现文件路径中包含 ..的情况
  global $config;
  foreach($config['dirs'] as $_dir){
      if(substr($fileName,0,strlen($_dir)) ==$_dir){
         return true;
      }
   }
  return false;
}

$is_safe=isInSafeDir($fileName);




//error_log($fileName."\t".(is_exclude($fileName)?"y":'n')."\n",3,dirname(__FILE__).'/log');

if(!$is_safe && !in_array($fn,array('fetch','meta'))){
   die_error("error:not allowed to visit:".$fileName);
}

//支持的命令
$allow_fn=array('put','meta','fetch','delete','mv');
if(!$fn ||!in_array($fn,$allow_fn)){
   die("error:unsupport request");
}
 
if($fn!='fetch' && is_exclude($fileName)){
  die('error:server exclude:'.$fileName);
}

if( 'put' == $fn ){
	dir_check(dirname($fileName));
	if(file_exists($fileName) && is_dir($fileName)){
	  $fileName.=DS.$data['baseName'];
	}
	if(!isset($data['content']))die_error("error:content mis");
	if(!isset($data['type']))die_error("error:type mis");
	$type=$data['type'];
	if( 'f' == $type ){
	  if(isset($data['i'])){
	     $tmpName=$fileName.".hsync_{$data['user']}.tmp";
	     file_put_contents($tmpName,$data['content'],$data['i']==1?LOCK_EX:FILE_APPEND);
	     if($data['i']==$data['m']){
	       rename($tmpName,$fileName);
	       call_upload_filish_sh($fileName);
	     }
	  }else{
	     file_put_contents($fileName,$data['content'],LOCK_EX);
	      call_upload_filish_sh($fileName);
	  }
	  die("ok");
	}else if( 'link' == $type ){
	   if(DS=="/"){
	     symlink($data['content'],$fileName);
	   }else{
	      //windows平台
		  die_error("error:unsupport link");
	   }
	}
	die("ok");
}else if( 'meta' == $fn ){
   if(!file_exists($fileName))die("");
   //是否递归获取子目录，默认是
   $recursive=isset($data['recursive'])?$data['recursive']:true;
  getMeta($fileName,$recursive);
}else if ('fetch' == $fn ){
   if(!file_exists($fileName))die_error("error:file mis($fileName)");
   _fetch($fileName);
}else if( 'delete' == $fn){
  if(!file_exists($fileName))die_error("error:file mis($fileName)");
  delete($fileName);
}else if('mv' == $fn){
  if(isset($data['remote_to']) || !isInSafeDir($data['remote_to']))die_error('param wrong!');
  dir_check(dirname($data['remote_to']));
  $ret=rename($fileName,$data['remote_to']);
  if($ret)die('ok');
  die_error('mv fail');
}
//print_r($fileName);
//$mtime=filemtime($fileName);


function dir_check($dir){
        return is_dir($dir) or (dir_check(dirname($dir)) and mkdir($dir, 0777));
}

function getMeta($file,$recursive=true){
    if(substr(basename($file),0,1)=="." || is_exclude($file))return;
    static $i=0;
	if(is_dir($file)){
	    _print_meta($file,'d');
         if(!$recursive && $i>0)return;
         $i++;
         $files=scandir($file,1);
		 foreach($files as $_fileName){
		   if(substr($_fileName,0,1)==".")continue;
			getMeta(rtrim($file,DS).DS.$_fileName,$recursive);
		  }
     }else if(is_file($file)){
	    _print_meta($file,'f');
	  }else if(is_link($file)){
	    _print_meta($file,'l');
	 }
}

function _print_meta($file,$type){
      $stat=stat($file);
      $arr=array();
	  $arr['path']=$file;
	  $arr['type']=$type;
	  $arr['md5']=md5_file($file);
	  $arr['ct']=$stat['ctime'];
	  $arr['mt']=$stat['mtime'];
	  $arr['size']=$stat['size'];
	  $arr['client_time']=$_POST['client_time'];
	  $arr['request_time']=$_SERVER['REQUEST_TIME'];
  echo json_encode($arr)."\n";
}

function _fetch($fileName){
  set_time_limit(0);
  if(is_dir($fileName)){
     header('fileType:dir');
	 die('ok');
  }else if(is_file($fileName)){
     $_size=filesize($fileName);
     header('Content-Type:application/octet-stream');
	 header("Accept-Ranges: bytes" );
	 header("Accept-Length: " .$_size );
	 header("Content-Disposition: attachment; filename=" .basename($fileName));
     $fp=fopen($fileName,'rb');
	 while(!feof($fp)){
	  echo fread($fp,8192);
	  flush();
	  ob_flush();
	 }
     exit;  	 
  }
}

//删除文件 删除空目录
function delete($filePath){
  if(is_dir($filePath)){
     if(@rmdir($filePath)){
         echo "ok";
     }else{
        die_error("delte fail:".$filePath," ,just can delete empty dir.");
     }
  }else if(is_file($filePath)){
   echo @unlink($filePath)?"ok":'error';
  }
}

//判断目录、文件是否被忽略了
function is_exclude($path){
   global $data;
   static $list=null;
   if(is_null($list)){
     $list=array();
     $exclude_config=explode("\n",trim(file_get_contents('./config/exclude')));
     $user_exclude_file=dirname(__FILE__)."/config/".$data['user']."/exclude";
    
     if(file_exists($user_exclude_file) && is_file($user_exclude_file)){
        $_tmp=explode("\n",trim(file_get_contents($user_exclude_file)));
        $exclude_config=array_merge($exclude_config,$_tmp);
     }
     $exclude_config[]=dirname(__FILE__);
     foreach($exclude_config as $_c){
        if(!(empty($_c) || in_array(substr($_c,0,1),array(";","#")) ) ) {
            $list[]="#^".str_replace("\*","\\w*",strtolower(preg_quote($_c)))."$#";
          }
      }
   }
   $basename=basename($path);
   foreach($list as $_c){
      if($basename==$_c || preg_match($_c,$basename) || preg_match($_c,$path)){
         return true;
      }
   }
   return false;
}

/*
 * 上传文件完成后执行的shell脚本
 */
function call_upload_filish_sh($path){
 while(!file_exists($filename=dirname($path).DS."hsync_call.sh") && isInSafeDir($filename)){
    $path=dirname($path);
 }
 if(isInSafeDir($filename) && file_exists($filename)){
    shell_exec("cd ".dirname($filename)." && sh ".$filename);
 }
}

function die_error($msg){
   @error_log($_POST['user']."\t".$msg."\n",3,dirname(__FILE__).'/log');
   @header('HTTP/1.1 403 Forbidden');
   echo $msg;
   die;
}
